/*
 * $Id: sck.c,v 1.12 2006-02-08 11:04:01 bacon Exp $
 */

#include <xp/net/sck.h>
#include <xp/bas/memory.h>
#include <xp/bas/sysapi.h>
#include <xp/bas/errno.h>
#include <xp/bas/assert.h>

xp_sck_t* xp_sck_open (xp_sck_t* sck, int domain, int type, int proto)
{
	xp_sckhnd_t hnd;

	if (sck == XP_NULL) {
		sck = (xp_sck_t*) xp_malloc (xp_sizeof(*sck));
		if (sck == XP_NULL) return XP_NULL;
		sck->__dynamic = xp_true;
	}
	else sck->__dynamic = xp_false;
	
	hnd = xp_sckopen (domain, type, proto);
	if (hnd == -1) {
		int num = xp_errno;
		if (sck->__dynamic) xp_free (sck);
		xp_seterrno (num);
		return XP_NULL;
	}
	
	sck->sckhnd = hnd;

	sck->conn_tmout = -1;
	sck->acpt_tmout = -1;
	sck->send_tmout = -1;
	sck->recv_tmout = -1;

	sck->conn_cancel = NULL;
	sck->conn_cancel_arg = NULL;

	return sck;
}

xp_sck_t* xp_sck_openhnd (xp_sck_t* sck, xp_sckhnd_t handle)
{
	if (sck == XP_NULL) {
		sck = (xp_sck_t*) xp_malloc (xp_sizeof(*sck));
		if (sck == XP_NULL) return XP_NULL;
		sck->__dynamic = xp_true;
	}
	else sck->__dynamic = xp_false;
	
	sck->sckhnd = handle;
	return sck;
}

int xp_sck_close (xp_sck_t* sck)
{
	int n;

	n = xp_sckclose (sck->sckhnd);
	if (n == -1) return -1;

	if (sck->__dynamic) xp_free (sck);
	else sck->sckhnd = XP_SCKHND_INVALID;

	return 0;
}

int xp_sck_forfeit (xp_sck_t* sck)
{
	/* do not close the handle */
	if (sck->__dynamic) xp_free (sck);
	else sck->sckhnd = XP_SCKHND_INVALID;
	return 0;
}

xp_sckhnd_t xp_sck_gethnd (xp_sck_t* sck)
{
	return sck->sckhnd;
}

xp_time_t xp_sck_gettmout (xp_sck_t* sck, int object)
{
	xp_assert (sck != XP_NULL);

	if (object & XP_SCK_CONN) return sck->conn_tmout;
	if (object & XP_SCK_ACPT) return sck->acpt_tmout;
	if (object & XP_SCK_SEND) return sck->send_tmout;
	if (object & XP_SCK_RECV) return sck->recv_tmout;

	return -1;
}

void xp_sck_settmout (xp_sck_t* sck, int object, xp_time_t tmout)
{
	xp_assert (sck != XP_NULL);

	if (object & XP_SCK_CONN) sck->conn_tmout = tmout;
	if (object & XP_SCK_ACPT) sck->acpt_tmout = tmout;
	if (object & XP_SCK_SEND) sck->send_tmout = tmout;
	if (object & XP_SCK_RECV) sck->recv_tmout = tmout;
}

void xp_sck_setcancel (xp_sck_t* sck, int object, xp_bool_t (*cancel) (void*), void* arg)
{
	xp_assert (sck != XP_NULL);

	if (object & XP_SCK_CONN) {
		sck->conn_cancel = cancel;
		sck->conn_cancel_arg = arg;
	}

	/* don't support cancellers for other objects */
}

int xp_sck_shtdwn (xp_sck_t* sck, int how)
{
	return xp_sckshtdwn (sck->sckhnd, how);
}

int xp_sck_conn (xp_sck_t* sck, const xp_sckadr_t* addr, xp_scklen_t len)
{
	return (sck->conn_cancel == NULL)?
		xp_sckconn (sck->sckhnd, addr, len, sck->conn_tmout):
		xp_sckconnx (sck->sckhnd, addr, len, sck->conn_tmout, 
			sck->conn_cancel, sck->conn_cancel_arg);
}

int xp_sck_bind (xp_sck_t* sck, xp_sckadr_t* addr, xp_scklen_t len)
{
	return xp_sckbind (sck->sckhnd, addr, len);
}

int xp_sck_lstn (xp_sck_t* sck, int backlog)
{
	return xp_scklstn (sck->sckhnd, backlog);
}

xp_sck_t* xp_sck_acpt (
	xp_sck_t* sck, xp_sck_t* newsck, xp_sckadr_t* addr, xp_scklen_t* len)
{
	xp_sckhnd_t hnd;

	hnd = xp_sckacpt (sck->sckhnd, addr, len, sck->acpt_tmout);
	if (hnd == XP_SCKHND_INVALID) return XP_NULL;

	newsck = xp_sck_openhnd (newsck, hnd);
	if (newsck == XP_NULL) xp_sckclose (hnd);
	return newsck;
}

xp_ssize_t xp_sck_recv (
	xp_sck_t* sck, void* buf, xp_size_t len, int flags)
{
	return xp_sckrecv (sck->sckhnd, buf, len, flags, sck->recv_tmout);
}

xp_ssize_t xp_sck_recvall (
	xp_sck_t* sck, void* buf, xp_size_t len, int flags)
{
	return xp_sckrecvall (sck->sckhnd, buf, len, flags, sck->recv_tmout);
}

xp_ssize_t xp_sck_send (
	xp_sck_t* sck, const void* msg, xp_size_t len, int flags)
{
	return xp_scksend (sck->sckhnd, msg, len, flags, sck->send_tmout);
}

xp_ssize_t xp_sck_sendall (
	xp_sck_t* sck, const void* msg, xp_size_t len, int flags)
{
	return xp_scksendall (sck->sckhnd, msg, len, flags, sck->send_tmout);
}

xp_ssize_t xp_sck_recvfrom (
	xp_sck_t* sck, void* buf, xp_size_t size, int flags,
	xp_sckadr_t* from, xp_scklen_t* fromlen)
{
	return xp_sckrecvfrom (
		sck->sckhnd, buf, size, flags, from, fromlen, sck->recv_tmout);
}

xp_ssize_t xp_sck_sendto (
	xp_sck_t* sck, const void* msg, xp_size_t len, int flags,
	const xp_sckadr_t* to, xp_scklen_t tolen)
{
	return xp_scksendto (
		sck->sckhnd, msg, len, flags, to, tolen, sck->send_tmout);
}

int xp_sck_getopt (xp_sck_t* sck, int level, 
	int optname, void* optval, xp_scklen_t* optlen)
{
	return xp_sckgetopt (sck->sckhnd, level, optname, optval, optlen);
}

int xp_sck_setopt (xp_sck_t* sck, int level,
    int optname, const void* optval, xp_scklen_t optlen)
{
	return xp_scksetopt (sck->sckhnd, level, optname, optval, optlen);
}

int xp_sck_getself (xp_sck_t* sck, xp_sckadr_t* name, xp_scklen_t* len)
{
	return xp_sckgetself (sck->sckhnd, name, len);
}

int xp_sck_getpeer (xp_sck_t* sck, xp_sckadr_t* name, xp_scklen_t* len)
{
	return xp_sckgetpeer (sck->sckhnd, name, len);
}
